<?php

namespace App\Exceptions;

use Exception;
use Response;
use Illuminate\Validation\ValidationException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Exception\HttpResponseException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that should not be reported.
     *
     * @var array
     */
    protected $dontReport = [
        AuthorizationException::class,
        HttpException::class,
        ModelNotFoundException::class,
        ValidationException::class,
        \LogicException::class,
    ];

    /**
     * Report or log an exception.
     *
     * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
     *
     * @param  \Exception  $e
     * @return void
     */
    public function report(Exception $e)
    {
        $eClass = get_class($e);
        $eMessage = $e->getMessage();
        if (!in_array($eClass, [
                'Symfony\Component\HttpKernel\Exception\NotFoundHttpException',
                'Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException',
                'Illuminate\Database\Eloquent\ModelNotFoundException',
                'Symfony\Component\Process\Exception\InvalidArgumentException',
                'Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException',
                'Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException',
                'Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException',
                'LogicException',
            ])
            and !in_array($eMessage, [
                '验证码已失效, 请重新获取',
                '用户名或密码错误',
                '请重新登录',
            ])) ding('error', "test report\n".$e->getCode() . "\n"
            . $e->getFile() . ' line ' . $e->getLine() . "\n"
            . "$eClass\n$eMessage"
        );

//        if (in_array($eClass, [
//            'Symfony\Component\HttpKernel\Exception\NotFoundHttpException',
//        ])
//        ) ding('route_test', "test report\n".$e->getCode() . "\n"
//            . $e->getFile() . ' line ' . $e->getLine() . "\n"
//            . "$eClass\n$eMessage"
//        );
        parent::report($e);
    }

    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $e
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $e)
    {
        $eClass = get_class($e);
        $eMessage = $e->getMessage();
        if (!in_array($eClass, [
                'Symfony\Component\HttpKernel\Exception\NotFoundHttpException',
                'Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException',
                'Illuminate\Database\Eloquent\ModelNotFoundException',
                'Symfony\Component\Process\Exception\InvalidArgumentException',
                'Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException',
                'Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException',
                'Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException',
                'LogicException',
            ])
            and !in_array($eMessage, [
                '验证码已失效, 请重新获取',
                '用户名或密码错误',
                '请重新登录',
            ])) ding('error', $e->getCode() . "\n"
            . $e->getFile() . ' line ' . $e->getLine() . "\n"
            . $request->url() . "\n"
            . "$eClass\n$eMessage"
        );
        if (in_array($eClass, [
            'Symfony\Component\HttpKernel\Exception\NotFoundHttpException',
        ])
        ) ding('route_test', $e->getCode() . "\n"
            . $e->getFile() . ' line ' . $e->getLine() . "\n"
            . $request->url() . "\n"
            . "$eClass\n$eMessage"
        );
        if (1 || $request->isJson()) {
            //todo 搞个中间件, 根据路由自动满足$request->isJson()
            if ($e instanceof HttpResponseException) {
                return $e->getResponse();
            } elseif ($e instanceof ModelNotFoundException) {
                $e = new NotFoundHttpException($e->getMessage(), $e);
            } elseif ($e instanceof AuthenticationException) {
                return $this->unauthenticated($request, $e);
            } elseif ($e instanceof AuthorizationException) {
                $e = new HttpException(403, $e->getMessage());
            } elseif ($e instanceof ValidationException && $e->getResponse()) {
                return $e->getResponse();
            }

            if ($this->isHttpException($e)) {
                $json = $this->jsonResponse($e->getStatusCode() + 50000, $e->getMessage(), $e);

                return Response::json($json, 200, [], 143);
            }

            $info = class_basename($e).' in '.basename($e->getFile()).' line '.$e->getLine().': '.$e->getMessage();
            switch (class_basename($e)) {
                case 'MethodNotAllowedHttpException':
                    $code = 50405;
                    break;
                case 'InvalidArgumentException':
                    $code = 50422;
                    $info = '参数无效: '.$e->getMessage();
                    break;
                case 'LogicException':
                    $code = 51000 + $e->getCode();
                    $info = $e->getMessage();
                    break;
                default:
                    $code = 50000;
                    break;
            }

            return Response::json($this->jsonResponse($code, $info, $e), 200, [], 143);
        } else {
            return parent::render($request, $e);
        }
    }

    static function jsonResponse($code, $info, $e)
    {
        $isShowTrace = config('app.debug');
        if ('LogicException' == class_basename($e)) {
            $isShowTrace = false;
        }
        $traces = [];
        if ($isShowTrace) {

            foreach ($e->getTrace() as $trace) {
                $data = [];
                if (data_get($trace, 'file')) {
                    $data['file'] = implode(' ', [data_get($trace, 'file'), data_get($trace, 'line')]);
                }
                if (data_get($trace, 'function')) {
                    $data['function'] = implode(' ', [data_get($trace, 'class'), data_get($trace, 'type'), data_get($trace, 'function')]);
                }

                $traces[] = $data;
            }

        }
        $data = $isShowTrace ? $traces : new \stdClass();

        return [
            'msg' => [
                'code' => $code,
                'info' => $info,
            ],
            'data' => $data,
        ];
    }
}
